package model.tokens;

import java.util.ArrayList;
import java.util.List;

import model.parser.ParserException;
import model.parser.ParsingData;


/**
 * Factory to produce tokens based on ParsingData and the input string
 * 
 * @author KevinWang Chris James Misha Lavrov
 *
 * 
 */
public class TokenFactory {

    List<ParsingData> myParsingDataList;
    
    public TokenFactory(List<ParsingData> parsingList)
	{
    	myParsingDataList = parsingList;
	}
        
    
    /**
     * Recursive method for tokenizing input.
     * 
     * Split the string by index-th parsingData object,
     * then parse the things in between.
     * 
     * @param input the String to be tokenized
     * @param index the entry into myParsingDataList to consider
     * @return the resulting list of tokens
     */
    public List<AbstractToken> tokenizeHelper(String input, int index)
    {
        List<AbstractToken> result = new ArrayList<AbstractToken>();

        if (input.equals(""))
            return result;
        
        if (index == myParsingDataList.size())
            throw ParserException.bad_token(input);
        
        ParsingData pd = myParsingDataList.get(index);
        
        String[] inputList = input.replaceAll(pd.getSymbol()," $0 ").split("\\s");
        
        for(String word: inputList)
        {
        	if(word.equals("")||word.equals("\\s"))
        		continue;
            if (pd.matchesSymbol(word))
                result.add(makeToken(word, pd));
            else
                result.addAll(tokenizeHelper(word, index+1));
        }
        
        return result;
    }
    
    
    /**
     * turn the string into tokens
     */
    public List<AbstractToken> tokenize(String input) 
    {
    	return tokenizeHelper(input, 0);
    	//return tokenizeHelper(input.replaceAll("\\s", ""), 0);
    }

    
    /**
     * For each string, use the ParsingData to identify if it is of the right syntax,
     * then use reflection to create the right type of Token.
     * @param input: string to be tokenized
     * @return AbstractToken of the matching type
     * @throws ParserException.BAD_TOKEN if no token matches the input
     */
	public AbstractToken makeToken(String input)
	{
    	for(ParsingData op : myParsingDataList){
    		if(op.matchesSymbol(input))
    		{
    		    return makeToken(input, op);
    		}
    	}
    	
		throw ParserException.bad_token(input);
    }


    /**
     * Make a token when you know what ParsingData matches it
     * 
     * @param input: the string to make a token out of
     * @param op: the ParsingData that matches it
     * @return AbstractToken of the matching type
     */

    public AbstractToken makeToken (String input, ParsingData op)
    {
        AbstractToken answer = null;
        
        try 
        {
            Class<?> tokenClass = op.getTokenClass();
            answer = (AbstractToken) 
                tokenClass.getConstructor(String.class, ParsingData.class).newInstance(input, op);
        } 
        catch (Exception e) 
        {
        	throw ParserException.BAD_REFLECTION;
        }
        
        return answer;
    }

}
